package com.winning.sx.microframework.plugins.wx;

import com.winning.sx.microframework.common.util.Constains;
import com.winning.sx.microframework.common.util.HttpRequestUtil;
import com.winning.sx.microframework.common.util.JsonUtils;
import net.sf.json.JSONObject;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.DefaultHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.BasicHttpClientConnectionManager;
import org.apache.http.util.EntityUtils;
import org.apache.log4j.Logger;
import org.junit.Test;
import org.springframework.web.context.ContextLoader;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.servlet.ServletContext;
import javax.xml.XMLConstants;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.*;
import java.net.URL;
import java.security.*;
import java.security.cert.CertificateException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;

/**
 * 微信
 *
 * @author lst
 */
public class WxUtil {
    private static Logger log = Logger.getLogger(WxUtil.class);

    public static Map postData(String urlPost, Map<String, String> bodyMap) {
        JSONObject receiverMap = HttpRequestUtil.httpPost(urlPost, bodyMap, false);
        return Integrate(receiverMap.toString());
    }

    public static Map Integrate(String data) {
        com.alibaba.fastjson.JSONObject jsonObject = (com.alibaba.fastjson.JSONObject) com.alibaba.fastjson.JSONObject.parse(data);
        Map ret = new HashMap();
        if (jsonObject.get("errcode") == null) {
            ret.put("code", "0000");
            ret.put("result", jsonObject.toString());
            return ret;
        }
        ret.put("code", getCode(jsonObject.get("errcode").toString()));
        ret.put("msg", jsonObject.get("errmsg").toString());
        ret.put("result", jsonObject.toString());
        return ret;
    }

    public static String getCode(String code) {
        switch (code) {
            case "null":
                return "0000";
            default:
                return "?" + code;
        }
    }


    /**
     * 设置订单里的支付信息    需要用以下的信息   换取返回预支付订单prepayId
     */
    public static String packPayInfo(String wayType, String ip, String orderid, String openid, String totalFee, String body) throws Exception {
        Map<String, String> map = new HashMap<String, String>();
        ////////////////////共有信息开始
        map.put("appid", WxConfig.appid);//微信支付分配的公众账号ID
        map.put("device_info", "");
        map.put("nonce_str", WXPayUtil.generateNonceStr());
        map.put("sign_type", WxConfig.sign_type);
        map.put("out_trade_no", orderid);//商户订单号
        map.put("fee_type", WxConfig.CNY);
        map.put("spbill_create_ip", ip);
        map.put("notify_url", WxConfig.notifyUrl);//通知地址
        map.put("trade_type", "JSAPI");
        map.put("limit_pay", "no_credit");
        map.put("total_fee", totalFee);
        map.put("body", body);
        map.put("openid", openid);
//        if (Constains.WAY_1.equals(wayType)) {
//            map.put("mch_id", WxConfig.mch_id);//微信支付分配的商户号
//            map.put("sign", WXPayUtil.generateSignature(map, WxConfig.key));//如果签名出错  是没有信息的
//        } else if (Constains.WAY_2.equals(wayType)) {
//            map.put("mch_id", WxConfig.mch_id2);//微信支付分配的商户号
//            map.put("sign", WXPayUtil.generateSignature(map, WxConfig.key2));//如果签名出错  是没有信息的
//
//        } else {
//            return null;
//        }
        String mapXmlStr = WXPayUtil.mapToXml(map);
        return mapXmlStr;
    }

    /**
     * 获取预支付订单id
     *
     * @param mapXmlStr
     * @return
     * @throws Exception
     */
    public static Map<String, String> getPrepayId(String mapXmlStr) throws Exception {
        //生成了统一的订单  将信息以post的方式发送微信服务器
        String backStr = httpsRequest(WxConfig.unifiedorder, "POST", mapXmlStr);
//        log.info("back str is " + URLDecoder.decode("UTF-8", backStr));
        Map<String, String> backMap = WXPayUtil.xmlToMap(backStr);
//        log.info("back prepay_id is " + backMap.get("prepay_id"));
        return backMap;
    }

    public static String httpsRequest(String requestUrl, String requestMethod, String output) {
        try {
            URL url = new URL(requestUrl);
            HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
            connection.setDoOutput(true);
            connection.setDoInput(true);
            connection.setUseCaches(false);
            connection.setRequestMethod(requestMethod);
            if (null != output) {
                OutputStream outputStream = connection.getOutputStream();
                outputStream.write(output.getBytes("UTF-8"));
                outputStream.close();
            }
            // 从输入流读取返回内容
            InputStream inputStream = connection.getInputStream();
            InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8");
            BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
            String str = null;
            StringBuffer buffer = new StringBuffer();
            while ((str = bufferedReader.readLine()) != null) {
                buffer.append(str);
            }
            bufferedReader.close();
            inputStreamReader.close();
            inputStream.close();
            inputStream = null;
            connection.disconnect();
            return buffer.toString();
        } catch (Exception ex) {
            ex.printStackTrace();
        }

        return "";
    }

    /**
     * XML格式字符串转换为Map
     *
     * @param strXML XML字符串
     * @return XML数据转换后的Map
     * @throws Exception
     */
    public static Map<String, String> xmlToMap(String strXML) throws Exception {
        try {
            Map<String, String> data = new HashMap<String, String>();
            DocumentBuilder documentBuilder = newDocumentBuilder();
            InputStream stream = new ByteArrayInputStream(strXML.getBytes("UTF-8"));
            org.w3c.dom.Document doc = documentBuilder.parse(stream);
            doc.getDocumentElement().normalize();
            NodeList nodeList = doc.getDocumentElement().getChildNodes();
            for (int idx = 0; idx < nodeList.getLength(); ++idx) {
                Node node = nodeList.item(idx);
                if (node.getNodeType() == Node.ELEMENT_NODE) {
                    org.w3c.dom.Element element = (org.w3c.dom.Element) node;
                    data.put(element.getNodeName(), element.getTextContent());
                }
            }
            try {
                stream.close();
            } catch (Exception ex) {
                // do nothing
                ex.printStackTrace();
            }
            return data;
        } catch (Exception ex) {
            WXPayUtil.getLogger().warn("Invalid XML, can not convert to map. Error message: {}. XML content: {}", ex.getMessage(), strXML);
            throw ex;
        }

    }

    public static DocumentBuilder newDocumentBuilder() throws ParserConfigurationException {
        DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
        documentBuilderFactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
        documentBuilderFactory.setFeature("http://xml.org/sax/features/external-general-entities", false);
        documentBuilderFactory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
        documentBuilderFactory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
        documentBuilderFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
        documentBuilderFactory.setXIncludeAware(false);
        documentBuilderFactory.setExpandEntityReferences(false);

        return documentBuilderFactory.newDocumentBuilder();
    }

    public static Document newDocument() throws ParserConfigurationException {
        return newDocumentBuilder().newDocument();
    }

    /**
     * 判断用户支付的结果有没有返回正确的信息  例如钓鱼什么的
     */
    public static Map<String, String> queryOrder(Map<String, String> map) throws Exception {
        Map<String, String> dataMap = new HashMap<String, String>();
        dataMap.put("appid", map.get("appid"));
        dataMap.put("mch_id", map.get("mch_id"));
        dataMap.put("transaction_id", map.get("transaction_id"));
        dataMap.put("out_trade_no", map.get("out_trade_no"));
        dataMap.put("sign_type", WxConfig.sign_type);
        dataMap.put("nonce_str", WXPayUtil.generateNonceStr());

        log.info(map.get("out_trade_no") + "采用住院配置");
        if (map.get("mch_id").equals(WxConfig.mch_id2))
            dataMap.put("sign", WXPayUtil.generateSignature(dataMap, WxConfig.key2));
        else
            dataMap.put("sign", WXPayUtil.generateSignature(dataMap, WxConfig.key));
        String backStr = httpsRequest(WxConfig.querydorder, "POST", WXPayUtil.mapToXml(dataMap));
        log.info(" query pay result is " + backStr);
        if (backStr != null && !"".equals(backStr)) {
            Map<String, String> orderMap = WXPayUtil.xmlToMap(backStr);
            //此处添加支付成功后，支付金额和实际订单金额是否等价，防止钓鱼
            if (orderMap.get("return_code") != null && orderMap.get("return_code").equalsIgnoreCase("SUCCESS")) {
                if (orderMap.get("trade_state") != null && orderMap.get("trade_state").equalsIgnoreCase("SUCCESS")) {
                    log.info(" pay result is ok!");
                    return orderMap;
                }
                return null;
            }
        }
        return null;
    }


    /**
     * 执行退款
     */
    public static boolean refundOrder(Map<String, String> map) throws Exception {

        Map<String, String> params = new HashMap<>();

        params.put("appid", map.get("appid"));
        params.put("mch_id", map.get("mch_id"));
        params.put("nonce_str", WXPayUtil.generateNonceStr());
        params.put("sign_type", WxConfig.sign_type);
        params.put("out_trade_no", map.get("orderCode"));
        params.put("out_refund_no", map.get("refundOrderCode"));
        params.put("total_fee", map.get("orderAmount"));// 订单金额
        params.put("refund_fee", map.get("refundAmount"));//
        params.put("refund_fee_type", WxConfig.CNY);
        params.put("refund_desc", map.get("hisOrderDescript"));
//        params.put("refund_account", "");
        params.put("notify_url", WxConfig.refundNotifyUrl);
        params.put("sign", WXPayUtil.generateSignature(params, map.get("mch_id").equals(WxConfig.mch_id2) ? WxConfig.key2 : WxConfig.key));
        String backStr = sendPostNeedCert(WXPayUtil.mapToXml(params),(map.get("mch_id").equals(WxConfig.mch_id2) ? WxConfig.apiclientCert2 : WxConfig.apiclientCert),map.get("mch_id"),WxConfig.refundorder );
        log.info("[registerFade:手动退号]微信患者手动退号:微信请求返回的数据是:" + backStr);
        if (backStr != null && !"".equals(backStr)) {
            Map<String, String> orderMap = WXPayUtil.xmlToMap(backStr);
            if ("SUCCESS".equals(orderMap.get("return_code"))) {
                if ("SUCCESS".equals(orderMap.get("result_code"))) {
                    log.info("退款成功：" + map.get("refundOrderCode") + "金额：" + map.get("refundAmount"));
                    return true;
                } else {
                    log.info("===========退款失败：" + map.get("refundOrderCode") + "金额：" + map.get("refundAmount") + "原因："
                            + orderMap.get("err_code_des") + orderMap.get("err_code"));
                    return false;
                }

            } else {
                log.info("============退款失败：" + map.get("refundOrderCode") + "金额：" + map.get("refundAmount"));
                return false;
            }
        } else {
            return false;
        }
    }

    public static String genRandomNum(int pwd_len) {
        // 35是因为数组是从0开始的，26个字母+10个数字
        final int maxNum = 10;
        int i; // 生成的随机数
        int count = 0; // 生成的密码的长度
        char[] str = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'};

        StringBuffer pwd = new StringBuffer("");
        Random r = new Random();
        while (count < pwd_len) {
            // 生成随机数，取绝对值，防止生成负数，
            i = Math.abs(r.nextInt(maxNum)); // 生成的数最大为maxNum-1
            if (i >= 0 && i < str.length) {
                pwd.append(str[i]);
                count++;
            }
        }
        return pwd.toString();
    }

    /***
     * 获得的是jssdk config
     * @return
     */
    public static Map getWxConfig(String url) {
        String config = "";
        try {
            String noncestr = genRandomNum(4);//随机字符串
            String timestamp = String.valueOf(System.currentTimeMillis() / 1000);//时间戳
            String str = "jsapi_ticket=" + WxConfig.jsapiTicket + "&noncestr=" + noncestr + "&timestamp=" + timestamp + "&url=" + url;
//			 debug: true, //调试阶段建议开启  
//			    appId: "APPID",//APPID  
//			    timestamp: "timestamp",//上面main方法中拿到的时间戳timestamp  
//			    nonceStr: "nonceStr",//上面main方法中拿到的随机数nonceStr  
//			    signature: "signature",//上面main方法中拿到的签名signature  
//			    jsApiList: [  
//			           //所有要调用的 API 都要加到这个列表中  
//			          "chooseImage"//从本地的相册、图库选择图片  
//			    ]  
            String signature = sha1Encode(str);
            Map<String, String> map = new HashMap();
            map.put("appId", WxConfig.appid);
            map.put("timestamp", timestamp);
            map.put("nonceStr", noncestr);
            map.put("signature", signature);

            return map;
        } catch (Exception e) {
            e.printStackTrace();
            log.info(" getWxConfig is error,error is --->" + e.getMessage());
            return new HashMap();
        }
    }

    /**
     * 发送模板消息
     *
     * @return
     */
    public static String sendTemplateInfo(List<String> sendInfoList) {
        String backStr = "";
        String accessToken = WxConfig.accessToken;
        //生成要传递的json对象
        Map<String, TemplateData> data = new HashMap<String, TemplateData>();
        Map send = new HashMap();
        if (sendInfoList != null && sendInfoList.size() > 0) {
            send.put("touser", sendInfoList.get(0));
            send.put("template_id", sendInfoList.get(1));
            if(sendInfoList.get(2) != null && sendInfoList.get(2) != ""){
                send.put("url",sendInfoList.get(2));
            }
            data.put("first", new TemplateData(sendInfoList.get(3)));
            data.put("remark", new TemplateData(sendInfoList.get(sendInfoList.size() - 1)));
            List<String> sonListStr = sendInfoList.subList(4, sendInfoList.size() - 1);
            if (sonListStr != null && sonListStr.size() > 0) {
                for (int i = 0; i < sonListStr.size(); i++) {
                    data.put("keyword" + (i + 1), new TemplateData(sonListStr.get(i)));
                }
            }
        }
        send.put("data", data);
        String sendStr = JsonUtils.objectToJson(send);
        log.info(" sendStr----->" + sendStr);
        backStr = httpsRequest(WxConfig.sendTemplateMessage + "?access_token=" + accessToken, "POST", sendStr);
        log.info("返回的消息是:" + backStr);
        return backStr;
    }

    public static String sha1Encode(String str) {
        System.out.println("Sha1:" + str);
        if (str == null) {
            return null;
        }
        try {
            MessageDigest messageDigest = MessageDigest.getInstance("SHA1");
            messageDigest.update(str.getBytes());
            return getFormattedText(messageDigest.digest());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    //SHA1
    private static final char[] HEX = {'0', '1', '2', '3', '4', '5',
            '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};

    private static String getFormattedText(byte[] bytes) {
        int len = bytes.length;
        StringBuilder buf = new StringBuilder(len * 2);
        // 把密文转换成十六进制的字符串形式
        for (int j = 0; j < len; j++) {
            buf.append(HEX[(bytes[j] >> 4) & 0x0f]);
            buf.append(HEX[bytes[j] & 0x0f]);
        }
        return buf.toString();
    }

    public static String getMoneyToY(String money) {
        money = Float.valueOf(money) / 100 + "";
        return money;
    }

    public static String getMoney(String money, boolean y) {
        if (y) {
            money = Float.valueOf(money) * 100 + "";
        }
        int i = money.indexOf(".");
        if (i > 0) {
            money = money.substring(0, i);
        }
        return money;
    }

    public static String getMoneyY(String money) {
        money = getMoney(money, false);
        if (money.length() == 1)
            return "0.0" + money;
        if (money.length() == 2)
            return "0." + money;
        if (money.length() > 2)
            return money.substring(0, money.length() - 2) + "." + money.substring(money.length() - 2);
        else
            return "";
    }
    public static String sendPostNeedCert(String mapXmlStr,String cert,String mch_id,String url)
            throws FileNotFoundException, KeyStoreException, IOException, NoSuchAlgorithmException,
            CertificateException, UnrecoverableKeyException, KeyManagementException, ClientProtocolException {
//        ServletContext context = ContextLoader.getCurrentWebApplicationContext().getServletContext();
//        String contextPath = context.getContextPath();
//        log.info("contextPath----------------->>" + contextPath);
        String targetPath = cert;
        InputStream certStream = new FileInputStream(targetPath);
        KeyStore ks = KeyStore.getInstance("PKCS12");
        ks.load(certStream, mch_id.toCharArray());
        KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
        kmf.init(ks, mch_id.toCharArray());

        SSLContext sslContext = SSLContext.getInstance("TLS");
        sslContext.init(kmf.getKeyManagers(), null, new SecureRandom());

        SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(sslContext,
                new String[] { "TLSv1" }, null, new DefaultHostnameVerifier());
        BasicHttpClientConnectionManager connManager = new BasicHttpClientConnectionManager(RegistryBuilder
                .<ConnectionSocketFactory>create().register("http", PlainConnectionSocketFactory.getSocketFactory())
                .register("https", sslConnectionSocketFactory).build(), null, null, null);
        HttpClient httpClient = HttpClientBuilder.create().setConnectionManager(connManager).build();

        // String downloadfundflowStr = HttpUtils.post(WxInfo.downloadfundflow,
        // mapXmlStr);
        HttpPost httpPost = new HttpPost(url);

        RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(10000).setConnectTimeout(2000).build();
        httpPost.setConfig(requestConfig);

        StringEntity postEntity = new StringEntity(mapXmlStr, "UTF-8");
        httpPost.addHeader("Content-Type", "text/xml");
        httpPost.addHeader("User-Agent", "wxpay sdk java v1.0 " + mch_id); // TODO:
        // 很重要，用来检测
        // sdk
        // 的使用情况，要不要加上商户信息？
        httpPost.setEntity(postEntity);

        HttpResponse httpResponse = httpClient.execute(httpPost);
        HttpEntity httpEntity = httpResponse.getEntity();
        String downloadfundflowStr = EntityUtils.toString(httpEntity, "UTF-8");
        return downloadfundflowStr;
    }

    @Test
    public void t() {
        String tt = "801.99";
        System.out.println(getMoney(tt, true));

        tt = "80199.00";
        System.out.println(getMoneyY(tt));
        tt = "9.00";
        System.out.println(getMoneyY(tt));
        tt = "10";
        System.out.println(getMoneyY(tt));
        tt = "502";
        System.out.println(getMoneyY(tt));
    }
}
